package cn.tedu.csmall.passport.security;

import cn.tedu.csmall.common.web.JsonResult;
import cn.tedu.csmall.common.web.State;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import org.omg.IOP.ServiceContextHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

/**
 * JWT过滤器,从请求头的Authorization中获取JWT中存入的用户信息,
 * 并添加到Spring Security上下文中,
 * 以致于Spring Security后续的组件（包括过滤器等）能从上下文中获取此用户的信息
 * 从而验证是否已经登录、是否具有权限等
 */

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {


    //密钥  至少得有四位  越乱越好,越长越好
    @Value("${csmall.jwt.secret-key}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //客户端提交请求时,应该在请求头的Authorization中添加JWT数据

        //尝试获取JWT数据
        String jwt = request.getHeader("Authorization");
        //清除 Spring Security上下文中的数据
        SecurityContextHolder.clearContext();



        System.out.println("从请求头中获取到的JWT="+jwt);
        //判断是否存在jwt数据 条件--->>>jwt==null||jwt.trim().equals("")
        if (!StringUtils.hasText(jwt)){
            //不存在jwt数据,则放行,后续还有其他过滤器及相关组件进行其他的处理,例如未登录则要求登录等
            //此处不宜直接阻止运行,因为"登录""注册"等请求本应该没有jwt数据

            System.out.println("请求头中无JWT数据,当前过滤器将放行");
            filterChain.doFilter(request,response);//继续执行过滤器链中后续的过滤器
            return;//一旦执行,必须return
        }

        //注意:此时执行时,如果请求头中携带了Authorization,日志中将输出,且不会有任何响应,因为当前过滤器没有放行!
        System.out.println("请求头中包含JWT,准备解析此数据......");
        //TODO 以下代码是有可能抛出异常的

        String username =null;
        String permissionsString =null;

        //TODO 密钥和各个Key应该统一定义
        try {
            Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
            username= claims.get(JwtConst.KEY_USERNAME).toString();
            permissionsString = claims.get(JwtConst.KEY_PERMISSIONS).toString();
            System.out.println("username=" + username);
            System.out.println("permissionsString=" + permissionsString);
        }catch(ExpiredJwtException e){
            System.out.println("解析JWT失败,此JWT已过期:"+e.getMessage());


            JsonResult<Void> jsonResult =JsonResult.fail(
                    State.ERR_JWT_EXPIRED,"你的登录已过期,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:"+jsonString);
            //设置文档类型
            response.setContentType("application/json;charset=utf-8");
            //响应  输出流
            response.getWriter().println(jsonString);
            return;
        }catch (MalformedJwtException e){
            System.out.println("解析JWT失败,此JWT数据错误,无法解析:"+e.getMessage());

            JsonResult<Void> jsonResult =JsonResult.fail(
                    State.ERR_JWT_MALFORMED,"获取信息有误,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:"+jsonString);
            //设置文档类型
            response.setContentType("application/json;charset=utf-8");
            //响应  输出流
            response.getWriter().println(jsonString);
            return;


        }catch (SignatureException e){
            System.out.println("解析JWT失败,此JWT签名错误:"+e.getMessage());

            JsonResult<Void> jsonResult =JsonResult.fail(
                    State.ERR_JWT_SIGNATURE,"获取信息有误,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:"+jsonString);
            //设置文档类型
            response.setContentType("application/json;charset=utf-8");
            //响应  输出流
            response.getWriter().println(jsonString);
            return;


        }catch (Throwable e){
            System.out.println("解析JWT失败,异常类型:"+e.getClass().getName());
            e.printStackTrace();

            JsonResult<Void> jsonResult =JsonResult.fail(
                    State.ERR_INTERNAL_SERVER_ERROR,"获取信息有误,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:"+jsonString);
            //设置文档类型
            response.setContentType("application/json;charset=utf-8");
            //响应  输出流
            response.getWriter().println(jsonString);
            return;



        }
        List<SimpleGrantedAuthority> permissions = JSON.parseArray(permissionsString, SimpleGrantedAuthority.class);
        //将解析得到的用户信息传递给Spring Security
        //获取Spring Security 的上下文,并将authentication放到上下文中
        //在Authentication中封装:用户名,null(密码),权限列表
        //因为接下来并不会处理认证,所以Authentication中不需要密码
        //后续,Spring Security 发现上下文中authentication时,就会视为已登录
        Authentication authentication  = new UsernamePasswordAuthenticationToken(username,null,permissions);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //放行
        filterChain.doFilter(request,response);
    }
}
